/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package util.math;

import com.jme3.math.Vector3f;

/**
 *
 * @author
 */
public class Vec {

    public static Vec zero() {
        return new Vec(0, 0, 0);
    }

    public static Vec unitX() {
        return new Vec(1, 0, 0);
    }

    public static Vec unitY() {
        return new Vec(0, 1, 0);
    }

    public static Vec unitZ() {
        return new Vec(0, 0, 1);
    }
    float x, y, z;

    public Vec() {
        this(0, 0, 0);
    }

    public Vec(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vec(Vec v) {
        setTo(v);
    }

    public Vec(Vector3f v) {
        this.x = v.getX();
        this.y = v.getY();
        this.z = v.getZ();
    }

    public final void setTo(Vec v) {
        this.x = v.x;
        this.y = v.y;
        this.z = v.z;
    }

    public float[] toFloatArray3() {
        return new float[]{this.x, this.y, this.z};
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public float getZ() {
        return z;
    }

    public void setX(float x) {
        this.x = x;
    }

    public void setY(float y) {
        this.y = y;
    }

    public void setZ(float z) {
        this.z = z;
    }

    public void addXLoc(float v) {
        this.x += v;
    }

    public void addYLoc(float v) {
        this.y += v;
    }

    public void addZLoc(float v) {
        this.z += v;
    }

    public void set(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vec subtract(Vec v) {
        return new Vec(x - v.x, y - v.y, z - v.z);
    }

    public void subtractFromX(float v) {
        this.x -= v;
    }

    public void subtractFromZ(float v) {
        this.z -= v;
    }

    public Vec add(Vec v) {
        return new Vec(x + v.x, y + v.y, z + v.z);
    }

    public Vec add(float x, float y, float z) {
        return new Vec(this.x + x, this.y + y, this.z + z);
    }

    public void multXLoc(float f) {
        this.x *= f;
    }

    public Vec halfWay(Vec other) {
        return this.add(other).mult(0.5f);
    }

    /**
     * We return the current object after modification, so that the operator can
     * be chained.
     *
     * @param v
     * @return
     */
    public Vec addLoc(Vec v) {
        this.x += v.x;
        this.y += v.y;
        this.z += v.z;

        return this;
    }

    public float length() {
        return (float) Math.sqrt(x * x + y * y + z * z);
    }

    public Vec normalize() {
        Vec v = new Vec(this);
        return v.normalizeLoc();
    }

    public Vec normalizeLoc() {
        float len = length();
        this.x /= len;
        this.y /= len;
        this.z /= len;

        return this;
    }

    public Vec towards(Vec v) {
        return new Vec(v.x - x, v.y - y, v.z - z);
    }

    public Vec percentageTowards(Vec v, float s) {
        return this.add(this.towards(v).mult(s));
    }

    /**
     * Use the right-hand rule to interpret the result: thumb -> x, index finger
     * -> y, middle finger -> result of cross product.
     *
     * Actually, another right-hand rule to use: curl fingers of right hand from
     * 'this' Vec to the passed vec -> the thumb gives the direction of the
     * cross product.
     */
    public Vec cross(Vec v) {
        return new Vec(
                y * v.z - z * v.y,
                z * v.x - x * v.z,
                x * v.y - y * v.x);
    }

    public float dot(Vec v) {
        //http://knol.google.com/k/dot-product-cross-product-in-3d#3%282E%29_Dot_product
        return this.x * v.x
                + this.y * v.y
                + this.z * v.z;
    }
        
    public float dot(float x, float y, float z) {
        return 
                this.x * x + this.y * y + this.z * z;
    }

    @Override
    public String toString() {
        return "(" + x + "," + y + "," + z + ")";
    }

    public Vec divided(float s) {
        float f = 1.0f / s;
        return mult(f);
    }

    public Vec mult(float f) {
        return new Vec(x * f, y * f, z * f);
    }

    public void subtractLoc(Vec c) {
        this.x -= c.x;
        this.y -= c.y;
        this.z -= c.z;
    }

    /**
     * Apply the result of the multiplication also locally.
     *
     * @param f
     * @return
     */
    public Vec multLoc(float f) {
        this.x *= f;
        this.y *= f;
        this.z *= f;

        return this;
    }

    public Vec divide(float f) {
        return multLoc(1.0f / f);
    }

    public String saveToString() {
        return "" + x + "," + y + "," + z;
    }

    public static Vec loadFromString(String s) {
        String[] pieces = s.split(",");

        float x = Float.parseFloat(pieces[0]);
        float y = Float.parseFloat(pieces[1]);
        float z = Float.parseFloat(pieces[2]);

        return new Vec(x, y, z);
    }

    public float distanceTo(Vec b) {
        return this.towards(b).length();
    }

    public static Vec average(Vec... vecs) {
        Vec avg = new Vec();

        for (Vec v : vecs) {
            avg.addLoc(v);
        }

        return avg.divide(vecs.length);
    }

    public Vec addLoc(float x, float y, float z) {
        this.x += x;
        this.y += y;
        this.z += z;

        return this;
    }

    public void setZero() {
        this.x = 0;
        this.y = 0;
        this.z = 0;
    }

    public Vector3f toMonkey() {
        return new Vector3f(x, y, z);
    }

    public void multZLoc(float s) {
        this.z *= s;
    }

    
}
